#if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014 Audiokinetic Inc. / All Rights Reserved
//
//////////////////////////////////////////////////////////////////////

using UnityEngine;
using System.Collections.Generic;
using System;

/// @brief Maintains the list of loaded SoundBanks loaded. This is currently used only with AkAmbient objects.
/// 

namespace AudioStudio
{
    public static class AkBankManager
    {
        class BankHandle
        {
            protected int m_RefCount = 0;
            protected uint m_BankID;
            protected float m_beginUnloadTime = 0.0f;

            public string bankName;

            public BankHandle(string name)
            {
                bankName = name;
            }

            public int RefCount
            {
                get
                {
                    return m_RefCount;
                }
            }

            public float BeginUnloadTime
            {
                get
                {
                    return m_beginUnloadTime;
                }
                set
                {
                    m_beginUnloadTime = value;
                }
            }

            override public bool Equals(System.Object obj)
            {
                BankHandle handle = (BankHandle)obj;
                return this.bankName.Equals(handle.bankName);
            }

            override public int GetHashCode()
            {
                return this.bankName.GetHashCode();
            }

            /// Loads a bank. This version blocks until the bank is loaded. See AK::SoundEngine::LoadBank for more information.
            public virtual AKRESULT DoLoadBank()
            {
                return AkSoundEngine.LoadBank(bankName, AkSoundEngine.AK_DEFAULT_POOL_ID, out m_BankID);
            }

            public void LoadBank()
            {
                if (m_RefCount == 0)
                {
                    AKRESULT res = DoLoadBank();
                    LogLoadResult(res);
                }

                IncRef();
            }

            /// Unloads a bank.
            public virtual void UnloadBank()
            {
                AkSoundEngine.UnloadBank(m_BankID, IntPtr.Zero, null, null);
            }

            public void IncRef()
            {
                m_RefCount++;
            }

            public void DecRef()
            {
                m_RefCount--;
            }

            protected void LogLoadResult(AKRESULT result)
            {
                if (result != AKRESULT.AK_Success)
                    AkSoundEngine.LogWarning("WwiseUnity: Bank " + bankName + " failed to load (" + result.ToString() + ")");
            }
        }

        class AsyncBankHandle : BankHandle
        {
            AkCallbackManager.BankCallback bankCallback = null;

            public AsyncBankHandle(string name, AkCallbackManager.BankCallback callback) : base(name)
            {
                bankCallback = callback;
            }

            static void GlobalBankCallback(uint in_bankID, IntPtr in_pInMemoryBankPtr, AKRESULT in_eLoadResult, uint in_memPoolId, object in_Cookie)
            {
                Lock();
                var handle = (AsyncBankHandle)in_Cookie;
                var callback = handle.bankCallback;
                if (in_eLoadResult != AKRESULT.AK_Success)
                {
                    handle.LogLoadResult(in_eLoadResult);
                    m_BankHandles.Remove(handle.bankName);
                }
                Unlock();

                if (callback != null)
                    callback(in_bankID, in_pInMemoryBankPtr, in_eLoadResult, in_memPoolId, null);
            }

            /// Loads a bank.  This version returns right away and loads in background. See AK::SoundEngine::LoadBank for more information
            public override AKRESULT DoLoadBank()
            {
                return AkSoundEngine.LoadBank(bankName, GlobalBankCallback, this, AkSoundEngine.AK_DEFAULT_POOL_ID, out m_BankID);
            }
        }

        static Dictionary<string, BankHandle> m_BankHandles = new Dictionary<string, BankHandle>();
        static List<BankHandle> BanksToUnload = new List<BankHandle>();
        static System.Threading.Mutex m_Mutex = new System.Threading.Mutex();
        public static bool ThreadSafe = false;
        public static float LastUnloadTime = 0; //每帧最多加载几个bank
        public static float DelayUnloadSeconds = 30.0f; //延迟多少时间卸载bank

        public static void PerframeUpdate()
        {
            if (BanksToUnload.Count > 0)
            {
                for (int i = BanksToUnload.Count - 1; i >= 0; i--)
                {
                    if (Time.time - BanksToUnload[i].BeginUnloadTime > DelayUnloadSeconds)
                    {
                        m_BankHandles.Remove(BanksToUnload[i].bankName);
                        BanksToUnload[i].UnloadBank();
                        BanksToUnload.RemoveAt(i);
                    }
                }
            }
        }

        public static void Reset()
        {
            m_BankHandles.Clear();
            BanksToUnload.Clear();
        }

        /// Loads a SoundBank. This version blocks until the bank is loaded. See AK::SoundEngine::LoadBank for more information.
        public static void LoadBank(string name)
        {
            Lock();
            BankHandle handle = null;
            if (!m_BankHandles.TryGetValue(name, out handle))
            {
                handle = new BankHandle(name);
                m_BankHandles.Add(name, handle);
                Unlock();
                handle.LoadBank();
            }
            else
            {
                // Bank already loaded, increment its ref count.
                handle.IncRef();
                RemoveFromUnload(handle);
                Unlock();
            }
        }

        /// Loads a SoundBank. This version returns right away and loads in background. See AK::SoundEngine::LoadBank for more information.
        public static void LoadBankAsync(string name, AkCallbackManager.BankCallback callback = null)
        {
            Lock();
            BankHandle handle = null;
            if (!m_BankHandles.TryGetValue(name, out handle))
            {
                AsyncBankHandle asyncHandle = new AsyncBankHandle(name, callback);
                m_BankHandles.Add(name, asyncHandle);
                Unlock();
                asyncHandle.LoadBank();
            }
            else
            {
                // Bank already loaded, increment its ref count.
                handle.IncRef();
                RemoveFromUnload(handle);
                Unlock();
            }
        }

        /// Unloads a SoundBank. See AK::SoundEngine::UnloadBank for more information.
        public static void UnloadBank(string name)
        {
            Lock();
            BankHandle handle = null;
            if (m_BankHandles.TryGetValue(name, out handle))
            {
                handle.DecRef();
                if (handle.RefCount == 0)
                {
                    handle.BeginUnloadTime = Time.time;
                    BanksToUnload.Add(handle);
                }
            }
            Unlock();
        }

        private static void RemoveFromUnload(BankHandle handle)
        {
            if (BanksToUnload.Contains(handle))
            {
                BanksToUnload.Remove(handle);
            }
        }

        public static void ForceUnloadAll()
        {
#if UNITY_EDITOR
        foreach(var i in m_BankHandles)
        {
            BanksToUnload.Add(i.Value);
        }
        m_BankHandles.Clear();

        foreach(var i in BanksToUnload)
		{
			i.UnloadBank();
		}
		BanksToUnload.Clear();
#endif
        }

        private static void Lock()
        {
            if (ThreadSafe)
            {
                m_Mutex.WaitOne();
            }
        }

        private static void Unlock()
        {
            if (ThreadSafe)
            {
                m_Mutex.ReleaseMutex();
            }
        }
    }
}
#endif // #if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.